/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.geekbang.projects.rest.core;


import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.*;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.*;


/**
 * @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
 * @since
 */
public class DefaultResponse extends HttpResponse {

	private int status;

	private Object entity;

	private int length;

	private Set<String> allowedMethods;


	private MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();

	private Locale locale;

	private MediaType mediaType;


	private Map<String, NewCookie> cookies = new HashMap<>();


	private Date lastModified;

	private URI location;

	private EntityTag entityTag;

	private Set<Link> links = new LinkedHashSet<>();

	private InputStream inputStream;


	private HttpURLConnection connection;


	@Override
	public int getStatus() {
		return status;
	}

	@Override
	public StatusType getStatusInfo() {
		return new DefaultStatusType(this.status);
	}

	@Override
	public Object getEntity() {
		return entity;
	}

	@Override
	public <T> T readEntity(Class<T> entityType) {
		return readEntity(new GenericType<>(entityType));
	}

	@Override
	public <T> T readEntity(GenericType<T> entityType) {
		if (this.entity != null) {
			return (T) this.entity;
		}
		EntityTag entityTag = getEntityTag();
		checkResponse(entityType.getRawType(), entityTag.getValue());
		//如果是json格式
		if (JSONValidator.from(entityTag.getValue()).validate()) {
			this.entity = (T) JSONObject.parseObject(entityTag.getValue(), entityType.getRawType());
		} else {
			this.entity = entityTag.getValue();
		}
		//如果不是json格式，则抛出异常

		return (T) this.entity;
	}

	/**
	 * 校验响应
	 *
	 * @param clazz 响应类型
	 */
	private void checkResponse(Class clazz, String result) {
		if (clazz != String.class) {
			//校验是否为json格式
			if (!JSONValidator.from(entityTag.getValue()).validate()) {
				throw new IllegalStateException(String.format("响应数据%s不是json格式，无法被实体化", result));
			}
			//如果是 */json */* 则支持json响应
			if (!MediaType.WILDCARD_TYPE.getSubtype().equals(this.mediaType.getSubtype())
					&& !MediaType.APPLICATION_JSON_TYPE.getSubtype().equals(this.mediaType.getSubtype())) {
				throw new IllegalStateException(String.format("响应类型%s无法支持响应内容实体化", this.mediaType.toString()));
			}
		}
	}

	@Override
	public <T> T readEntity(Class<T> entityType, Annotation[] annotations) {
		throw new UnsupportedOperationException("DefaultResponse#entityType 暂无实现");
	}

	@Override
	public <T> T readEntity(GenericType<T> entityType, Annotation[] annotations) {
		throw new UnsupportedOperationException("DefaultResponse#entityType 暂无实现");
	}

	@Override
	public boolean hasEntity() {
		return entity != null;
	}

	@Override
	public boolean bufferEntity() {
		return entity != null;
	}

	@Override
	public void close() {
		IOUtils.closeQuietly(this.inputStream);
	}

	@Override
	public MediaType getMediaType() {
		return mediaType;
	}

	@Override
	public Locale getLanguage() {
		return locale;
	}

	@Override
	public int getLength() {
		return length;
	}

	@Override
	public Set<String> getAllowedMethods() {
		return allowedMethods;
	}

	@Override
	public Map<String, NewCookie> getCookies() {
		return cookies;
	}

	@Override
	public EntityTag getEntityTag() {
		try {
			//初始化流
			if (this.inputStream == null) {
				this.inputStream = connection.getInputStream();
			}
			//初始化实体标签
			if (this.entityTag != null) {
				return this.entityTag;
			}
			String value = IOUtils.toString(inputStream);
			this.entityTag = new EntityTag(value);
			return this.entityTag;
		} catch (Exception e) {
			throw new IllegalStateException(e);
		}
	}

	@Override
	public Date getDate() {
		return lastModified;
	}

	@Override
	public Date getLastModified() {
		return lastModified;
	}

	@Override
	public URI getLocation() {
		return location;
	}

	@Override
	public Set<Link> getLinks() {
		return links;
	}

	@Override
	public boolean hasLink(String relation) {
		return false;
	}

	@Override
	public Link getLink(String relation) {
		return null;
	}

	@Override
	public Link.Builder getLinkBuilder(String relation) {
		return null;
	}

	@Override
	@Deprecated
	public MultivaluedMap<String, Object> getMetadata() {
		return null;
	}

	@Override
	public MultivaluedMap<String, String> getStringHeaders() {
		return this.headers;
	}

	@Override
	public String getHeaderString(String name) {
		return this.headers.getFirst(name);
	}

	public void setAllowedMethods(HashSet<String> allowedMethods) {
		this.allowedMethods = allowedMethods;
	}

	public void setCookies(Map<String, NewCookie> cookies) {
		this.cookies = cookies;
	}

	public void setHeaderString(MultivaluedHashMap<String, String> headers) {
		this.headers = headers;
	}

	public void setLanguage(Locale locale) {
		this.locale = locale;
	}

	public void setLastModified(Date lastModified) {
		this.lastModified = lastModified;
	}

	public void setLength(int length) {
		this.length = length;
	}

	public void setLocation(String location) {
		if (StringUtils.isEmpty(location)) {
			return;
		}
		this.location = URI.create(location);
	}

	public void setMediaType(MediaType mediaType) {
		this.mediaType = mediaType;
	}

	public void setStatus(int status) {
		this.status = status;
	}

	@Override
	public void setConnection(HttpURLConnection connection) {
		this.connection = connection;
	}


	@Override
	public String toString() {
		return "DefaultResponse{" +
				"status=" + status +
				", entity=" + entity +
				", length=" + length +
				", allowedMethods=" + allowedMethods +
				", headers=" + headers +
				", locale=" + locale +
				", mediaType=" + mediaType +
				", cookies=" + cookies +
				", lastModified=" + lastModified +
				", location=" + location +
				", entityTag=" + entityTag +
				", links=" + links +
				", inputStream=" + inputStream +
				"} " + super.toString();
	}
}
